﻿#region License
// (c) Intergen.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using TextGlow.Control.Utilities;
using TextGlow.Control.Packaging;

namespace TextGlow.Control.Model
{
  public class Column
  {
    private Unit? _width;
    private Unit? _spacing;

    public Unit? Width
    {
      get { return _width; }
      set { _width = value; }
    }

    public Unit? Spacing
    {
      get { return _spacing; }
      set { _spacing = value; }
    }

    public Column(XElement element)
    {
      if (element == null)
        throw new ArgumentNullException("element");

      XNamespace ns = element.Name.Namespace;

      _width = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "w")));
      _spacing = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "space")));
    }
  }

  public enum BorderDisplay
  {
    allPages,
    firstPage,
    notFirstPage
  }

  public enum BorderOffsetFrom
  {
    page,
    text
  }

  public enum BorderZOrder
  {
    front,
    back
  }

  public enum Orientation
  {
    portrait,
    landscape
  }

  public enum SectionType
  {
    Continuous,
    EvenPage,
    NextColumn,
    NextPage,
    OddPage
  }

  public class SectionProperties : ContentProperties
  {
    private bool? _bidi;
    private List<Column> _columns = new List<Column>();
    private bool? _columnsEqualWidth;
    private bool? _columnSeperator;
    private int? _columnSpacing;
    private Border _borderTop;
    private Border _borderLeft;
    private Border _borderRight;
    private Border _borderBottom;
    private BorderDisplay? _borderDisplay;
    private BorderOffsetFrom? _borderOffsetFrom;
    private BorderZOrder? _borderZOrder;
    private Unit? _marginBottom;
    private Unit? _marginFooter;
    private Unit? _marginGutter;
    private Unit? _marginHeader;
    private Unit? _marginLeft;
    private Unit? _marginRight;
    private Unit? _marginTop;
    private Unit? _pageHeight;
    private Unit? _pageWidth;
    private Orientation? _pageOrientation;
    private SectionType? _type;
    private string _verticalAlignment;
    private string _footerDefaultId;
    private string _footerFirstId;
    private string _footerEvenId;
    private string _headerDefaultId;
    private string _headerFirstId;
    private string _headerEvenId;
    private bool? _titlePage;

    public string HeaderDefaultId
    {
      get { return _headerDefaultId; }
      set { _headerDefaultId = value; }
    }

    public Header HeaderDefault
    {
      get { return GetHeader(_headerDefaultId); }
    }

    public string HeaderFirstId
    {
      get { return _headerFirstId; }
      set { _headerFirstId = value; }
    }

    public Header HeaderFirst
    {
      get { return GetHeader(_headerFirstId); }
    }

    public string HeaderEvenId
    {
      get { return _headerEvenId; }
      set { _headerEvenId = value; }
    }

    public Header HeaderEven
    {
      get { return GetHeader(_headerEvenId); }
    }

    public string FooterDefaultId
    {
      get { return _footerDefaultId; }
      set { _footerDefaultId = value; }
    }

    public Footer FooterDefault
    {
      get { return GetFooter(_footerDefaultId); }
    }

    public string FooterFirstId
    {
      get { return _footerFirstId; }
      set { _footerFirstId = value; }
    }

    public Footer FooterFirst
    {
      get { return GetFooter(_footerFirstId); }
    }

    public string FooterEvenId
    {
      get { return _footerEvenId; }
      set { _footerEvenId = value; }
    }

    public Footer FooterEven
    {
      get { return GetFooter(_footerEvenId); }
    }

    public string VerticalAlignment
    {
      get { return _verticalAlignment; }
      set { _verticalAlignment = value; }
    }

    private SectionType? Type
    {
      get { return _type; }
      set { _type = value; }
    }

    public Unit? PageHeight
    {
      get { return _pageHeight; }
      set { _pageHeight = value; }
    }

    public Unit? PageWidth
    {
      get { return _pageWidth; }
      set { _pageWidth = value; }
    }

    public Orientation? PageOrientation
    {
      get { return _pageOrientation; }
      set { _pageOrientation = value; }
    }

    public Unit? MarginFooter
    {
      get { return _marginFooter; }
      set { _marginFooter = value; }
    }

    public Unit? MarginGutter
    {
      get { return _marginGutter; }
      set { _marginGutter = value; }
    }

    public Unit? MarginHeader
    {
      get { return _marginHeader; }
      set { _marginHeader = value; }
    }

    public Unit? MarginLeft
    {
      get { return _marginLeft; }
      set { _marginLeft = value; }
    }

    public Unit? MarginRight
    {
      get { return _marginRight; }
      set { _marginRight = value; }
    }

    public Unit? MarginTop
    {
      get { return _marginTop; }
      set { _marginTop = value; }
    }

    public Unit? MarginBottom
    {
      get { return _marginBottom; }
      set { _marginBottom = value; }
    }

    public BorderZOrder? BorderZOrder
    {
      get { return _borderZOrder; }
      set { _borderZOrder = value; }
    }

    public BorderOffsetFrom? BorderOffsetFrom
    {
      get { return _borderOffsetFrom; }
      set { _borderOffsetFrom = value; }
    }

    public BorderDisplay? BorderDisplay
    {
      get { return _borderDisplay; }
      set { _borderDisplay = value; }
    }

    public Border BorderBottom
    {
      get { return _borderBottom; }
      set { _borderBottom = value; }
    }

    public Border BorderRight
    {
      get { return _borderRight; }
      set { _borderRight = value; }
    }

    public Border BorderLeft
    {
      get { return _borderLeft; }
      set { _borderLeft = value; }
    }

    public Border BorderTop
    {
      get { return _borderTop; }
      set { _borderTop = value; }
    }

    public int? ColumnSpacing
    {
      get { return _columnSpacing; }
      set { _columnSpacing = value; }
    }

    public bool? ColumnSeperator
    {
      get { return _columnSeperator; }
      set { _columnSeperator = value; }
    }

    public bool? ColumnsEqualWidth
    {
      get { return _columnsEqualWidth; }
      set { _columnsEqualWidth = value; }
    }

    public bool? TitlePage
    {
      get { return _titlePage; }
      set { _titlePage = value; }
    }

    public SectionProperties(Content parentContent, XElement element)
      : base(parentContent)
    {
      if (element == null)
        throw new ArgumentNullException("element");

      XNamespace ns = element.Name.Namespace;

      _bidi = XmlUtils.GetValue<bool>(element.Element(ns + "bidi"));

      foreach (XElement columnElement in element.Elements("cols").Elements("col"))
      {
        _columns.Add(new Column(columnElement));
      }

      _columnSeperator = XmlUtils.OnOffToBoolean(XmlUtils.GetValue(element.Element(ns + "cols"), "sep"));
      _columnSpacing = XmlUtils.GetValue<int>(element.Element(ns + "cols"), "space");

      XElement pageBorders = element.Element(ns + "pgBorders");
      if (pageBorders != null)
      {
        _borderTop = Border.Create(this,
          pageBorders.Element(ns + "top"),
          null);
        _borderBottom = Border.Create(this,
          pageBorders.Element(ns + "bottom"),
          null);
        _borderLeft = Border.Create(this,
          pageBorders.Element(ns + "left"),
          null);
        _borderRight = Border.Create(this,
          pageBorders.Element(ns + "right"),
          null);

        _borderDisplay = XmlUtils.GetValue<BorderDisplay>(pageBorders, "display");
        _borderOffsetFrom = XmlUtils.GetValue<BorderOffsetFrom>(pageBorders, "offsetFrom");
        _borderZOrder = XmlUtils.GetValue<BorderZOrder>(pageBorders, "zOrder");
      }

      _headerDefaultId = GetSegmentRelationshipId(element, "headerReference", "default");
      _headerEvenId = GetSegmentRelationshipId(element, "headerReference", "even");
      _headerFirstId = GetSegmentRelationshipId(element, "headerReference", "first");

      _footerDefaultId = GetSegmentRelationshipId(element, "footerReference", "default");
      _footerEvenId = GetSegmentRelationshipId(element, "footerReference", "even");
      _footerFirstId = GetSegmentRelationshipId(element, "footerReference", "first");

      _marginBottom = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgMar"), "bottom"));
      _marginFooter = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgMar"), "footer"));
      _marginGutter = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgMar"), "gutter"));
      _marginHeader = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgMar"), "header"));
      _marginLeft = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgMar"), "left"));
      _marginRight = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgMar"), "right"));
      _marginTop = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgMar"), "top"));

      _pageHeight = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgSz"), "h"));
      _pageWidth = Unit.Twip(XmlUtils.GetValue<int>(element.Element(ns + "pgSz"), "w"));
      _pageOrientation = XmlUtils.GetValue<Orientation>(element.Element(ns + "pgSz"), "orient");

      _type = XmlUtils.GetValue<SectionType>(element.Element(ns + "type"));

      _verticalAlignment = XmlUtils.GetValue(element.Element(ns + "vAlign"));

      _titlePage = XmlUtils.GetBoolean(element.Element(ns + "titlePg"));
    }

    private string GetSegmentRelationshipId(XElement element, string elementName, string type)
    {
      XNamespace ns = element.Name.Namespace;

      return element.Elements(ns + elementName)
        .Where(e => (string)e.Attribute(ns + "type") == type)
        .Select(e => (string)e.Attribute(Document.Relationship + "id"))
        .SingleOrDefault();
    }

    private Header GetHeader(string headerRelationshipId)
    {
      if (Content == null)
        throw new InvalidOperationException("No associated section.");

      if (headerRelationshipId == null)
        return null;

      return Content.MainDocument.Headers[headerRelationshipId];
    }

    private Footer GetFooter(string headerRelationshipId)
    {
      if (Content == null)
        throw new InvalidOperationException("No associated section.");

      if (headerRelationshipId == null)
        return null;

      return Content.MainDocument.Footers[headerRelationshipId];
    }

    public static SectionProperties Create(Content content, XElement element)
    {
      if (element == null)
        return null;

      return new SectionProperties(content, element);
    }
  }
}